Dr. Jürgen Neumann contributes support for Medion GoPal.
authorrobertl <robertl>
Sat, 26 Jul 2008 17:27:12 +0000 (17:27 +0000)
committerrobertl <robertl>
Sat, 26 Jul 2008 17:27:12 +0000 (17:27 +0000)
Makefile.in
gopal.c [new file with mode: 0644]
testo
vecs.c
xmldoc/formats/gopal.xml [new file with mode: 0644]
xmldoc/formats/options/gopal-clean.xml [new file with mode: 0644]
xmldoc/formats/options/gopal-date.xml [new file with mode: 0644]
xmldoc/formats/options/gopal-maxspeed.xml [new file with mode: 0644]
xmldoc/formats/options/gopal-minspeed.xml [new file with mode: 0644]

index 8a2c91931769ecb05e9a7e7416a30ffb55245e19..6bf04afdc6ae7ba1b8e6583914addde1511c5368 100644 (file)
@@ -59,7 +59,7 @@ ALL_FMTS=$(MINIMAL_FMTS) gtm.o gpsutil.o pcx.o cetus.o copilot.o \
        wbt-200.o stmsdf.o gtrnctr.o dmtlog.o raymarine.o alan.o vitovtt.o \
        ggv_log.o g7towin.o garmin_gpi.o lmx.o random.o xol.o dg-100.o \
        navilink.o mtk_logger.o ik3d.o osm.o destinator.o exif.o vidaone.o \
-       igo8.o
+       igo8.o gopal.o
 
 FMTS=@FMTS@
 
diff --git a/gopal.c b/gopal.c
new file mode 100644 (file)
index 0000000..7f6d0fe
--- /dev/null
+++ b/gopal.c
@@ -0,0 +1,396 @@
+/*
+
+
+       Copyright (C) 2008  Dr. Jürgen Neumann, Juergen.Neumann@online.de
+       Copyright (C) 2005  Robert Lipe, robertlipe@usa.net (based on nmea.c)
+       Copyright (C) 20XX  probably many others from the gpsbabel development team ;-)
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation; either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to the Free Software
+       Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+       =====================================================================================
+
+       This file allows gpsbabel to read and write the internal track log format used by 
+       GoPal navigation systems. They produce a simple line-oriented format with one point per 
+       second. Unfortunately the the data does not contain a valid date, only some kind of timetick,
+       together with each point (perhaps by mistake ??). So we have to parse the filename for a valid starting
+       date and add the timeoffset. Second problem (at least to me) was that irregularly stupid errors were 
+       in the data, i.e. only one data point shows a totally wrong longitude or latitude. Everything else in 
+       the dataset seems ok, so I needed a way to sort out these errors. My solution is to calculate the speed
+       between successive points and drop points not between minspeed and maxspeed. This way I can sort out most
+       of this annoying bugs, a side effect is that if a minimum speed > 0 is set points with the same coodinates are also 
+       dropped.
+
+       Fileformat GoPal
+       TICK;   TIME UTC; LONG;    LAT;       HEIGHT; SPEED km/h;  FIX; HDOP;     SAT
+       3801444, 080558, 2.944362, 43.262117, 295.28, 0.12964, 2, 2.900000, 3 
+       Filenames:
+               trackYYYYMMDD_HHMMSS.trk
+               A_YYYYMMDD_HHMMSS.trk
+       with HHMMSS local time (not UTC)
+       
+       History
+       2008-07-18 initial release of Version 0.1
+       2008-07-26 bugfix: filenamehandling linux, format specification in write statement
+
+       ToDo:
+       - check for midnight & adjust
+*/
+
+#include "defs.h"
+#include <ctype.h>
+#include "csv_util.h"
+#include <time.h>
+#include "strptime.h"
+#include "jeeps/gpsmath.h"
+#include "grtcirc.h"
+#define MYNAME "gopal"
+
+static gbfile *fin, *fout;
+
+static struct tm tm,filenamedate, trackdate;
+time_t         tx;
+char tmp[64];
+char routename[64];
+static char *optdate=NULL;
+static char *optmaxspeed=NULL;
+static char *optminspeed=NULL;
+static char *optclean= NULL;
+static double minspeed,maxspeed;
+static struct tm opt_tm;       /* converted "date" parameter */
+static
+arglist_t gopal_args[] = {
+       {"date", &optdate, "Complete date-free tracks with given date (YYYYMMDD).", NULL, ARGTYPE_INT, ARG_NOMINMAX },
+       {"maxspeed", &optmaxspeed, "The maximum speed (km/h) traveling from waypoint to waypoint.", "200", ARGTYPE_INT, "1", "1000" },
+       {"minspeed", &optminspeed, "The minimum speed (km/h) traveling from waypoint to waypoint.\nset >0 to remove duplicate waypoints", "0", ARGTYPE_INT, "0", "999" },
+       {"clean", &optclean, "Cleanup common errors in trackdata", "1", ARGTYPE_BOOL, ARG_NOMINMAX },
+       ARG_TERMINATOR
+};
+
+#define CHECK_BOOL(a) if (a && (*a == '0')) a = NULL
+
+int gopal_check_line(char *line)
+{
+       char *c = line;
+       int i = 0;
+       while ((c = strchr(c, ',')))
+       {
+               c++;
+               i++;
+       }
+       if (i != 8)
+       {
+               snprintf(tmp,sizeof(tmp),"\"%s\"\n",line);
+               fprintf(stderr,tmp);
+       }
+       return i;
+}
+
+
+
+/*******************************************************************************
+* %%%        global callbacks called by gpsbabel main process              %%% *
+*******************************************************************************/
+
+static void
+gopal_rd_init(const char *fname)
+{char buff[32];
+       char *ck;
+       char *filename;
+       CHECK_BOOL(optclean);
+       if (optminspeed) 
+       {
+               minspeed=atof(optminspeed);
+               if (global_opts.debug_level > 1) fprintf(stderr,"options from command line : gopal minspeed = %s\n",optminspeed);
+       }
+       else
+       minspeed=0;
+       if (optmaxspeed) 
+       {
+               maxspeed=atof(optmaxspeed);
+               if (global_opts.debug_level > 1) fprintf(stderr,"options from command line : gopal maxspeed = %s\n",optmaxspeed);
+       }
+       else
+       maxspeed=200;
+       if (global_opts.debug_level > 1) fprintf(stderr,"setting minspeed to %5.1lf km/h and maxspeed to %5.1lf km/h\n",minspeed,maxspeed);
+
+       fin = gbfopen(fname, "r", MYNAME);
+       memset(buff,0,sizeof(buff));
+       if (optdate)
+       {  
+               memset(&opt_tm, 0, sizeof(opt_tm));
+               
+               ck = (char *)strptime(optdate, "%Y%m%d", &opt_tm);
+               if ((ck == NULL) || (*ck != '\0') || (strlen(optdate) != 8))
+               fatal(MYNAME ": Invalid date \"%s\"!\n", optdate);
+               else if (opt_tm.tm_year < 70)
+               fatal(MYNAME ": Date \"%s\" is out of range (have to be 19700101 or later)!\n", optdate);
+               tx = mkgmtime(&opt_tm);
+               
+       }
+       else
+       {
+               /* remove path */
+               filename = get_filename(fname);
+               
+               if ((strncmp(filename,"track",5)==0)&&(strlen(filename)>13)) // we need at least 13 letters: trackYYYYMMDD...
+               {
+                       strncpy(&buff[0],&filename[5],8);
+               }
+               else
+               if ((strncmp(filename,"A_",2)==0)&&(strlen(filename)>10))// here we expect at least 10 letters: A_YYYYMMDD...
+               {
+                       strncpy(&buff[0],&filename[2],8);
+               }
+               // in buff we should now have something wich looks like a valid date starting with YYYYMMDD
+               ck = (char *)strptime(buff, "%Y%m%d", &filenamedate);
+               if (((ck == NULL) || (*ck != '\0') )&&!(optdate))
+               fatal(MYNAME ": Invalid date in filename \"%s\", try to set manually using \"optdate\" switch!\n", buff);
+               else if (filenamedate.tm_year < 70)
+               fatal(MYNAME ": Date \"%s\" is out of range (have to be 19700101 or later)!\n", buff);
+               tx= mkgmtime(&filenamedate);
+       }
+}
+
+static void 
+gopal_rd_deinit(void)
+{
+       gbfclose(fin);
+}
+
+static void
+gopal_read(void)
+{
+
+       char *buff;
+       char *str, *c;
+       int column;
+       long line;
+       double hmsd,speed;
+       int fix, hms;
+       route_head *route;
+       waypoint *wpt, *lastwpt=NULL;
+       double long_old,lat_old;
+       char tbuffer[64];
+       long_old=0;lat_old=0;
+       strftime(routename,sizeof(routename),"Tracklog %c",localtime(&tx));
+       
+       route = route_head_alloc();
+       route->rte_name=xstrdup(routename);
+       route_add_head(route);
+
+       line=0;
+       while ((buff = gbfgetstr(fin)))
+       {
+               str = buff = lrtrim(buff);
+               if (*buff == '\0') continue;    
+               if (gopal_check_line(buff)!=8)continue;
+               wpt = waypt_new();
+               
+               column = -1;
+               // the format of gopal is quite simple. Unfortunately the developers forgot the date as the first element...
+               //TICK;    TIME;   LONG;     LAT;       HEIGHT; SPEED;  Fix; HDOP;    SAT
+               //3801444, 080558, 2.944362, 43.262117, 295.28, 0.12964, 2, 2.900000, 3 
+               c = csv_lineparse(str, ",", "", column++);
+               while (c != NULL)
+               {
+                       switch(column)
+                       {
+                       case  0: /* "-" */      /* unknown fields for the moment */
+                               //sscanf(c, "%llu", &wpt->microseconds);
+                               break;
+                       case  1:                                /* Time UTC */  
+                               sscanf(c,"%lf",&hmsd);
+                               hms = (int) hmsd;
+                               tm.tm_sec = hms % 100;
+                               hms = hms / 100;
+                               tm.tm_min = hms % 100;
+                               hms = hms / 100;
+                               tm.tm_hour = hms % 100;  
+                               tm.tm_year=trackdate.tm_year;
+                               tm.tm_mon=trackdate.tm_mon;
+                               tm.tm_mday=trackdate.tm_mday;
+                               wpt->creation_time = tx+((((time_t)tm.tm_hour * 60) + tm.tm_min) * 60) + tm.tm_sec;
+                               if (global_opts.debug_level > 1){
+                                       strftime(tbuffer, sizeof(tbuffer), "%c", gmtime(&wpt->creation_time));
+                                       printf("parsed timestamp: %s\n",tbuffer);       
+                               }                               
+                               break;
+                               
+                       case  2:                                /* longitude */
+                               sscanf(c, "%lf", &wpt->longitude);
+                               break;
+                               
+                       case  3:                                /* latitude */
+                               sscanf(c, "%lf", &wpt->latitude);
+                               break;
+                       case  4:                                /* altitude */
+                               sscanf(c, "%lf", &wpt->altitude);
+                               break;
+                       case  5:                                /* speed */
+                               //sscanf(c, "%lf", &wpt->speed);
+                               wpt->speed=atof(c);
+                               if (global_opts.debug_level > 1){
+                                       printf("parsed speed: %8.5f\n",wpt->speed);     
+                               }
+                               break;
+                       case  6:                                /* type of fix */
+                               sscanf(c, "%d", &fix);
+                               //my device shows only 0 or 2
+                               //should i guess from no of sats if 2d or 3d?
+                               switch (fix) {
+                               case 0: wpt->fix = fix_none;break;
+                               case 2: wpt->fix = fix_2d;break;
+                                       //case 3: wpt->fix = fix_3d;break;
+                                       //case 4: wpt->fix = fix_dgps;break; /* 2D_diff */
+                                       //case 5: wpt->fix = fix_dgps;break; /* 3D_diff */
+                               default:
+                                       wpt->fix = fix_unknown;
+                                       break;
+                               }
+                               break;
+                       case  7:                                /* hdop */
+                               wpt->hdop = atof(c);
+                               //sscanf(c, "%lf", &wpt->hdop); does not work ???
+                               //wpt->vdop=0;wpt->hdop=0;
+                               break;
+                       case  8:                                /* number of sats */
+                               sscanf(c, "%d", &wpt->sat);
+                               break;
+                               
+                       }
+                       c = csv_lineparse(NULL, ",", "", column++);
+               }
+               line++;
+               
+               if ((wpt->fix != fix_none)&&(lat_old==0)){ //first-time init
+                       lat_old=wpt->latitude;
+                       long_old=wpt->longitude;
+                       //route_add_wpt(route, wpt);
+                       lastwpt=wpt;
+               }
+               //calculate the speed to reach this waypoint from the last. This way I try to sort out invalid waypoints
+               speed=0;        
+               if (lastwpt !=NULL)
+               {
+                       speed=3.6*radtometers(gcdist(RAD(lastwpt->latitude), RAD(lastwpt->longitude), RAD(wpt->latitude), RAD(wpt->longitude))) / abs(wpt->creation_time - lastwpt->creation_time);     
+                       //printf("speed line %d %lf \n",line,speed);                    
+               }
+               /* Error handling: in the tracklog of my device sometimes "jump" waypoints ;-) */
+               if      ((optclean) && 
+               (((wpt->longitude==0.0)|| (wpt->latitude==0.0)||(abs(wpt->latitude)>90)||(abs(wpt->longitude)>180))||
+               ((speed>maxspeed)||(speed<minspeed)))
+               )
+               {
+                       if (global_opts.debug_level > 1) fprintf(stderr,"Problem in or around line %5lu: \"%s\" %lf km/h\n",line,buff,speed);
+               }
+               else
+               {
+                       if (global_opts.debug_level > 1) fprintf(stderr,"valid                line %5lu: \"%s\" %lf km/h\n",line,buff,speed);
+                       lastwpt=wpt;
+                       long_old=wpt->longitude;
+                       lat_old=wpt->latitude;
+                       route_add_wpt(route,wpt);
+                       waypt_add(waypt_dupe( wpt));
+               }
+       }
+}
+
+static void 
+gopal_route_hdr(const route_head *route)
+{
+       
+}
+
+static void 
+gopal_route_tlr(const route_head *rte)
+{
+}
+static void
+gopal_write_waypt(const waypoint *wpt)
+{
+       char tbuffer[64];
+       int fix=fix_unknown;
+       //TICK;    TIME;   LONG;     LAT;       HEIGHT; SPEED;  UN; HDOP;     SAT
+       //3801444, 080558, 2.944362, 43.262117, 295.28, 0.12964, 2, 2.900000, 3 
+       strftime(tbuffer, sizeof(tbuffer), "%H%M%S", gmtime(&wpt->creation_time));
+       if (wpt->fix!=fix_unknown) {
+               switch (wpt->fix) 
+               {
+               case fix_none:          fix = 0; break;
+               case fix_2d:            fix = 2; break;
+               default:                fix = 0; break;
+               }
+       }
+       gbfprintf(fout, "%lu, %s, %lf, %lf, %5.1lf, %8.5lf, %d, %lf, %d\n", (unsigned long) wpt->creation_time,tbuffer,  wpt->longitude, wpt->latitude,wpt->altitude,
+       wpt->speed,fix,wpt->hdop,wpt->sat);
+}
+
+
+static void
+gopal_wr_init(const char *fname)
+{
+       fout = gbfopen(fname, "w", MYNAME);
+}
+
+static void
+gopal_wr_deinit(void)
+{
+       gbfclose(fout);
+}
+
+static void
+gopal_write(void)
+{
+       //route_disp_all(gopal_route_hdr, gopal_route_tlr, gopal_write_waypt);  
+       if (global_opts.objective == wptdata) 
+       {
+               waypt_disp_all(gopal_write_waypt);
+               
+       } else
+       //if (global_opts.objective == rtedata) 
+       {
+               route_disp_all(gopal_route_hdr, gopal_route_tlr, gopal_write_waypt);
+       }
+}
+
+static void
+gopal_exit(void)               /* optional */
+{
+}
+
+/**************************************************************************/
+
+// capabilities below means: we can only read and write waypoints
+//  
+
+ff_vecs_t gopal_vecs = {
+       ff_type_file,
+       { 
+               ff_cap_read | ff_cap_write              /* waypoints */, 
+               ff_cap_none                                             /* tracks */, 
+               ff_cap_read | ff_cap_write      /* routes */
+       },
+       gopal_rd_init,  
+       gopal_wr_init,  
+       gopal_rd_deinit,        
+       gopal_wr_deinit,        
+       gopal_read,
+       gopal_write,
+       gopal_exit,
+       gopal_args,
+       CET_CHARSET_ASCII, 0    /* ascii is the expected character set */
+       /* not fixed, can be changed through command line parameter */
+};
+/**************************************************************************/
diff --git a/testo b/testo
index d6013a39371e2c961cda024b8a98ac9260253c9d..95fa0902943e6e2b1aa25d40a60fb457a0d65733 100755 (executable)
--- a/testo
+++ b/testo
@@ -1351,4 +1351,23 @@ ${PNAME} -i gpx -f ${TMPDIR}/igo.gpx -o igo8 -F ${TMPDIR}/new-igo.trk
 ${PNAME} -i igo8 -f ${TMPDIR}/new-igo.trk -o gpx -F ${TMPDIR}/new-igo2.gpx
 compare ${TMPDIR}/igo.gpx ${TMPDIR}/new-igo2.gpx
 
+
+# GoPal
+# GoPal is a bit tricky, because it's a lossy format regarding the first field (timetick).
+# so we first read a reference gopal file, and then write it out as gopal trk file, now with a computed timestamp.
+# Doing so results in more useful timestamps.
+# Next we convert this tst file to gpx to test the writing path, but gpx does not save speed, so do it twice...
+GoPalName=track20080703_173036.trk 
+rm -f ${TMPDIR}/${GoPalName}.*
+#step 1: reference file
+${PNAME} -i gopal -f ${REFERENCE}/track20080703_173036.trk -o gopal -F ${TMPDIR}/${GoPalName}.tst
+#step2: gpx without speed information
+${PNAME} -i gopal -f ${TMPDIR}/${GoPalName}.tst -o gpx  -F ${TMPDIR}/${GoPalName}.im
+${PNAME} -i gpx -f ${TMPDIR}/${GoPalName}.im  -o gopal -F ${TMPDIR}/${GoPalName}.tst2
+#step 3: do it again to have 2 identical gopal files:
+${PNAME} -i gopal -f ${TMPDIR}/${GoPalName}.tst -o gpx  -F ${TMPDIR}/${GoPalName}.im2
+${PNAME} -i gpx -f ${TMPDIR}/${GoPalName}.im2  -o gopal -F ${TMPDIR}/${GoPalName}.tst3
+compare ${TMPDIR}/${GoPalName}.tst2 ${TMPDIR}/${GoPalName}.tst3
+
+
 exit 0
diff --git a/vecs.c b/vecs.c
index 4f6dba0e591952ebe1939d303d04a7b42a8c30f9..86b5006ada280ba342f61de2bcae1fe2469d1834 100644 (file)
--- a/vecs.c
+++ b/vecs.c
@@ -140,6 +140,7 @@ extern ff_vecs_t destinator_trl_vecs;
 extern ff_vecs_t destinator_itn_vecs;
 extern ff_vecs_t exif_vecs;
 extern ff_vecs_t vidaone_vecs;
+extern ff_vecs_t gopal_vecs;
 
 static
 vecs_t vec_list[] = {
@@ -795,6 +796,12 @@ vecs_t vec_list[] = {
                "IGO8 .trk",
                "trk"
        },
+       {
+                &gopal_vecs,
+                "gopal",
+                "GoPal GPS track log (.trk)",
+               "trk"
+        },
 #endif // MAXIMAL_ENABLED
        {
                NULL,
diff --git a/xmldoc/formats/gopal.xml b/xmldoc/formats/gopal.xml
new file mode 100644 (file)
index 0000000..41c0fcb
--- /dev/null
@@ -0,0 +1,39 @@
+<para>The gopal format is a track format written by the
+ <ulink url="http://www.mediongopal.de/">
+   GoPal Navigation</ulink> program. </para>
+
+<para>
+  The format of the file itself is quite simple:</para>
+    <para>
+      <programlisting>
+        Fileformat GoPal
+        TICK;   TIME UTC; LONG;    LAT;       HEIGHT; SPEED km/h;  FIX; HDOP;     SAT
+        3801444, 080558, 2.944362, 43.262117, 295.28, 0.12964, 2, 2.900000, 3
+      </programlisting>
+
+Filenames are automatically created by the device as follows:
+      <programlisting>
+        trackYYYYMMDD_HHMMSS.trk  </programlisting>
+      and/or, depending on software version and settings:
+      <programlisting>
+               A_YYYYMMDD_HHMMSS.trk
+              </programlisting>
+       with HHMMSS local time (not UTC!)
+      </para> 
+      <para> 
+The file format lacks the correct date value for each trackpoint, so it has to be computed starting from the
+date given in the filename. To handle files without a parsable date in the
+name, the command line option 'date' is provided.
+</para>
+<para>
+Conversions from gopal into any other format are a bit lossy concerning the first field of each line wich looks like an useless time tick value
+and is simply discarded while reading. If written, there will be a time_t value from the current (computed) timestamp.
+</para>
+<para>
+
+Filtering out invalid data points is handled by the options 'clean', 'minspeed' and 'maxspeed'.
+For each datapoint the speed needed to come the from the last valid point is
+calculated and compared with the limits.
+Thus one cat easily discard unreliable points.
+
+         </para>
diff --git a/xmldoc/formats/options/gopal-clean.xml b/xmldoc/formats/options/gopal-clean.xml
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/xmldoc/formats/options/gopal-date.xml b/xmldoc/formats/options/gopal-date.xml
new file mode 100644 (file)
index 0000000..e3c6abd
--- /dev/null
@@ -0,0 +1,3 @@
+<para>
+  This option is useful if you have tracks from Gopal that have been renamed.
+</para>
diff --git a/xmldoc/formats/options/gopal-maxspeed.xml b/xmldoc/formats/options/gopal-maxspeed.xml
new file mode 100644 (file)
index 0000000..06122fc
--- /dev/null
@@ -0,0 +1,3 @@
+<para>
+  This is useful for removing wildly erroneous data from your recorded track.
+</para>
diff --git a/xmldoc/formats/options/gopal-minspeed.xml b/xmldoc/formats/options/gopal-minspeed.xml
new file mode 100644 (file)
index 0000000..13cf020
--- /dev/null
@@ -0,0 +1,3 @@
+<para>
+  If this is greater than zero, duplicate waypoints will be removed.
+</para>